package sky.cin.framework.network.http;

import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class Request {
    
    private Method method;
    private String path;
    private String version;
    private Map<String, String> headers = new ConcurrentHashMap<>();
    private Map<String, Cookie> cookies = new ConcurrentHashMap<>();
    private Map<String, String> parameters = new ConcurrentHashMap<>();

    public void init() {
        this.method = Method.GET;
        this.version = "HTTP/1.1";
        this.path = "";
    }

    public Request() {
        init();
    }

    public Request(String requestTxtOrUrl) {
        String[] requestTxtLine = requestTxtOrUrl.split("\r\n");
        if (requestTxtLine.length == 1 && requestTxtOrUrl.startsWith("/")) {
            init();
            this.path = requestTxtOrUrl;
            return;
        }
        // 解析请求行
        String[] requestLine = requestTxtLine[0].split(" ");
        this.method = Method.valueOf(requestLine[0].toUpperCase());
        this.path = requestLine[1];
        this.version = requestLine[2];
        // 解析请求头
        for (int index = 1; index < requestTxtLine.length; index++) {
            this.setHeader(requestTxtLine[index]);
        }
        // 解析请求参数
        switch (this.method) {
            case GET: {
                int index = this.path.indexOf("?");
                if (index >= 0) {
                    // 有请求参数
                    String parameterStr = this.path.substring(index + 1);
                    this.path = this.path.substring(0, index);
                    Arrays.stream(parameterStr.split("&")).forEach(this::setParameter);
                }
            }
        }
    }

    public Request(Request request) {
        setRequest(request);
    }

    public void setRequest(Request request) {
        this.method = request.method;
        this.path = request.path;
        this.version = request.version;
        this.headers = request.headers;
        this.cookies = request.cookies;
        this.parameters = request.parameters;
    }

    public void setMethod(Method method) {
        this.method = method;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    public void setHeaders(Map<String, String> headers) {
        this.headers = headers;
    }

    public void setHeader(String key, String value) {
        if ("cookie".equals(key)) {
            Arrays.stream(value.split("; ")).forEach(this::setCookie);
            return;
        }
        this.headers.remove(key);
        this.headers.put(key.toLowerCase(), value);
    }

    public void setHeader(String line) {
        String[] kv = line.split(": ");
        this.setHeader(kv[0], kv[1]);
    }

    public void setParameters(Map<String, String> parameters) {
        this.parameters = parameters;
    }

    private void setParameter(String key, String value) {
        this.parameters.put(key, value);
    }

    private void setParameter(String line) {
        String[] kv = line.split("=");
        this.setParameter(kv[0], kv[1]);
    }

    public void setCookies(Map<String, Cookie> cookies) {
        this.cookies = cookies;
    }

    private void setCookie(String key, String value) {
        Cookie cookie = new Cookie(key, value);
        this.cookies.put(key, cookie);
    }

    private void setCookie(String line) {
        String[] kv = line.split("=");
        this.setCookie(kv[0], kv[1]);
    }

    public Method getMethod() {
        return method;
    }
    
    public String getPath() {
        return path;
    }
    
    public String getVersion() {
        return version;
    }
    
    public Map<String, String> getHeaders() {
        return headers;
    }

    public String[] getHeaderKeys() {
        return headers.keySet().toArray(new String[0]);
    }

    public String getHeader(String key) {
        return headers.get(key.toLowerCase());
    }

    public Cookie[] getCookies() {
        return cookies.values().toArray(new Cookie[0]);
    }

    public String[] getCookieKeys() {
        return cookies.keySet().toArray(new String[0]);
    }

    public Cookie getCookie(String name) {
        return cookies.get(name);
    }

    public Map<String, String> getParameters() {
        return parameters;
    }

    public String[] getParameterKeys() {
        return parameters.keySet().toArray(new String[0]);
    }
    
    public String getParameter(String argName) {
        return parameters.get(argName);
    }
    
    public String toString() {
        String requestLine = String.format("%s %s %s\r\n", method.name(), path, version);
        StringBuilder builder = new StringBuilder(requestLine);
        headers.forEach((k, v) -> builder.append(k).append(": ").append(v).append("\r\n"));
        return builder.toString();
    }

    public byte[] getBytes() {
        return toString().getBytes(StandardCharsets.UTF_8);
    }
    
}
